/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2002-2006
* Sleepycat Software. All rights reserved.
*
* $Id: Java5LatchImpl.java,v 1.1 2006/05/06 09:00:33 ckaestne Exp $
*/
package com.sleepycat.je.latch;
import java.util.concurrent.locks.ReentrantLock;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.dbi.EnvironmentImpl;
/**
* Java5LatchImpl provides an implementation of the Latch interface. By using
* a wrapper class we can avoid link errors when we run in Java 1.4 JVMs.
* LatchSupport.java will only reference this class if it knows that the
* ReentrantLock class is available at runtime through Class.forName().
* LatchSupport only references this class through the Latch interface and only
* constructs an instance using
*
* Class.forName("Java5LatchImpl").newInstance();
*/
class Java5LatchImpl implements Latch {
/*
* Required because getOwner() is protected (for unknown reasons) and can't
* be accessed except by a subclass of ReentrantLock.
*/
static private class JEReentrantLock extends ReentrantLock {
JEReentrantLock(boolean fair) {
super(fair);
}
protected Thread getOwner() {
return super.getOwner();
}
}
private JEReentrantLock lock;
private String name;
private LatchStats stats = new LatchStats();
Java5LatchImpl() {
lock = new JEReentrantLock(EnvironmentImpl.getFairLatches());
}
/**
* Set the latch name, used for latches in objects instantiated from
* the log.
*/
public void setName(String name) {
this.name = name;
}
/**
* Acquire a latch for exclusive/write access.
*
* <p>Wait for the latch if some other thread is holding it. If there are
* threads waiting for access, they will be granted the latch on a FIFO
* basis. When the method returns, the latch is held for exclusive
* access.</p>
*
* @throws LatchException if the latch is already held by the calling
* thread.
*/
public void acquire()
throws DatabaseException {
try {
if (lock.isHeldByCurrentThread()) {
stats.nAcquiresSelfOwned++;
throw new LatchException(name + " already held");
}
if (lock.isLocked()) {
stats.nAcquiresWithContention++;
} else {
stats.nAcquiresNoWaiters++;
}
lock.lock();
assert noteLatch(); // intentional side effect;
} finally {
assert EnvironmentImpl.maybeForceYield();
}
}
/**
* Acquire a latch for exclusive/write access, but do not block if it's not
* available.
*
* @return true if the latch was acquired, false if it is not available.
*
* @throws LatchException if the latch is already held by the calling
* thread.
*/
public boolean acquireNoWait()
throws LatchException {
try {
if (lock.isHeldByCurrentThread()) {
stats.nAcquiresSelfOwned++;
throw new LatchException(name + " already held");
}
boolean ret = lock.tryLock();
if (ret) {
assert noteLatch();
stats.nAcquireNoWaitSuccessful++;
} else {
stats.nAcquireNoWaitUnsuccessful++;
}
return ret;
} finally {
assert EnvironmentImpl.maybeForceYield();
}
}
/**
* Release the latch. If there are other thread(s) waiting for the latch,
* one is woken up and granted the latch. If the latch was not owned by
* the caller, just return;
*/
public void releaseIfOwner() {
doRelease(false);
}
/**
* Release the latch. If there are other thread(s) waiting for the latch,
* they are woken up and granted the latch.
*
* @throws LatchNotHeldException if the latch is not currently held.
*/
public void release()
throws LatchNotHeldException {
if (doRelease(true)) {
throw new LatchNotHeldException(name + " not held");
}
}
/**
* Do the work of releasing the latch. Wake up any waiters.
*
* @returns true if this latch was not owned by the caller.
*/
private boolean doRelease(boolean checkHeld) {
try {
if (!lock.isHeldByCurrentThread()) {
return true;
}
lock.unlock();
stats.nReleases++;
assert unNoteLatch(checkHeld); // intentional side effect.
} catch (IllegalMonitorStateException IMSE) {
return true;
}
return false;
}
/**
* Return true if the current thread holds this latch.
*
* @return true if we hold this latch. False otherwise.
*/
public boolean isOwner() {
return lock.isHeldByCurrentThread();
}
/**
* Used only for unit tests.
*
* @return the thread that currently holds the latch for exclusive access.
*/
public Thread owner() {
return lock.getOwner();
}
/**
* Return the number of threads waiting.
*
* @return the number of threads waiting for the latch.
*/
public int nWaiters() {
return lock.getQueueLength();
}
/**
* @return a LatchStats object with information about this latch.
*/
public LatchStats getLatchStats() {
LatchStats s = null;
try {
s = (LatchStats) stats.clone();
} catch (CloneNotSupportedException e) {
/* Klockwork - ok */
}
return s;
}
/**
* Formats a latch owner and waiters.
*/
public String toString() {
return lock.toString();
}
/**
* Only call under the assert system. This records latching by thread.
*/
private boolean noteLatch()
throws LatchException {
return LatchSupport.latchTable.noteLatch(this);
}
/**
* Only call under the assert system. This records latching by thread.
*/
private boolean unNoteLatch(boolean checkHeld) {
/* Only return a false status if we are checking for latch ownership.*/
if (checkHeld) {
return LatchSupport.latchTable.unNoteLatch(this, name);
} else {
LatchSupport.latchTable.unNoteLatch(this, name);
return true;
}
}
}